home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / c / etc / ttyPdev.c < prev    next >
C/C++ Source or Header  |  1990-02-28  |  14KB  |  495 lines

  1. /* 
  2.  * ttyPdev.c --
  3.  *
  4.  *    This file provides a bridge between the pdev and td modules to
  5.  *    produce a pseudo-device that behaves like a terminal.
  6.  *
  7.  * Copyright 1989 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/ttyPdev.c,v 1.8 90/02/28 11:10:55 brent Exp $ SPRITE (Berkeley)";
  19. #endif /* not lint */
  20.  
  21. #include <pdev.h>
  22. #include <status.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <td.h>
  26.  
  27. /*
  28.  * Library imports:
  29.  */
  30.  
  31. extern void panic();
  32.  
  33. /*
  34.  * One of the following structures is created for each call to
  35.  * Td_CreatePdev:
  36.  */
  37.  
  38. typedef struct {
  39.     Pdev_Token pdev;            /* Token returned by Pdev_Open. */
  40.     char *pdevName;            /* Name of pseudo-device file
  41.                      * (malloc'ed). */
  42.     Td_Terminal term;            /* Token returned by Td_Create. */
  43.     int selectState;            /* Current select state of terminal;
  44.                      * a combination of FS_READABLE,
  45.                      * FS_WRITABLE, and FS_EXCEPTION. */
  46. } PdevTerm;
  47.  
  48. /*
  49.  * Forward declarations to procedures defined later in this file:
  50.  */
  51.  
  52. static int    ChangeReady();
  53. static int    CookedProc();
  54. static int    PdevClose();
  55. static int    PdevIoctl();
  56. static int    PdevOpen();
  57. static int    PdevRead();
  58. static int    PdevWrite();
  59. static int    SendSignal();
  60.  
  61. /*
  62.  *----------------------------------------------------------------------
  63.  *
  64.  * Td_CreatePdev --
  65.  *
  66.  *    Create a terminal with a pseudo-device attached to it.
  67.  *
  68.  * Results:
  69.  *    The return value is a handle that may be passed to
  70.  *    Td_DeletePdev to close the pseudo-terminal.  The Td_Terminal
  71.  *    token for the terminal gets stored at *termPtr, for the
  72.  *    caller's use in communicating with the terminal driver.
  73.  *    If a pseudo-device couldn't be opened, then the return value
  74.  *    is NULL and an error message is stored in pdev_ErrorMsg.
  75.  *
  76.  * Side effects:
  77.  *    A Td_Terminal is created with its "cooked" side attached
  78.  *    to a pseudo-device managed by this module.  The caller
  79.  *    must use the Fs_Select facilities so that this module
  80.  *    gets callbacks from the Pdev library.
  81.  *
  82.  *----------------------------------------------------------------------
  83.  */
  84.  
  85. Td_Pdev
  86. Td_CreatePdev(name, realNamePtr, termPtr, rawProc, clientData)
  87.     char *name;            /* Name of file to use for pseudo-device. */
  88.     char **realNamePtr;        /* Where to store pointer to actual name
  89.                  * used. */
  90.     Td_Terminal *termPtr;    /* Token for the Td_Terminal gets written
  91.                  * here, if this is non-NULL. */
  92.     int (*rawProc)();        /* Procedure for Td module to call to
  93.                  * handle control requests on raw side
  94.                  * of terminal. */
  95.     ClientData clientData;    /* Arbitrary data value to pass to rawProc. */
  96. {
  97.     Pdev_CallBacks callbacks;
  98.     register PdevTerm *ptPtr;
  99.  
  100.     ptPtr = (PdevTerm *) malloc(sizeof(PdevTerm));
  101.     callbacks.open = PdevOpen;
  102.     callbacks.read = PdevRead;
  103.     callbacks.write = PdevWrite;
  104.     callbacks.ioctl = PdevIoctl;
  105.     callbacks.close = PdevClose;
  106.     ptPtr->pdev = Pdev_Open(name, realNamePtr, 1000, 0,
  107.         &callbacks, (ClientData) ptPtr);
  108.     if (ptPtr->pdev == NULL) {
  109.     free((char *) ptPtr);
  110.     return (Td_Pdev) NULL;
  111.     }
  112.     if (realNamePtr != NULL) {
  113.     name = *realNamePtr;
  114.     }
  115.     ptPtr->pdevName = malloc((unsigned) (strlen(name) + 1));
  116.     strcpy(ptPtr->pdevName, name);
  117.     ptPtr->term = Td_Create(1000, CookedProc, (ClientData) ptPtr,
  118.         rawProc, clientData);
  119.     ptPtr->selectState = FS_WRITABLE;
  120.  
  121.     if (termPtr != NULL) {
  122.     *termPtr = ptPtr->term;
  123.     }
  124.     return (Td_Pdev) ptPtr;
  125. }
  126.  
  127. /*
  128.  *----------------------------------------------------------------------
  129.  *
  130.  * Td_DeletePdev --
  131.  *
  132.  *    Delete a pseudo-device and the Td_Terminal associated with it.
  133.  *
  134.  * Results:
  135.  *    None.
  136.  *
  137.  * Side effects:
  138.  *    The memory and state associated with the pseudo device and
  139.  *    terminal are recycled.  The pseudo-device file is destroyed,
  140.  *    if that is possible.
  141.  *
  142.  *----------------------------------------------------------------------
  143.  */
  144.  
  145. void
  146. Td_DeletePdev(ttyPdev)
  147.     Td_Pdev ttyPdev;        /* Pseudo-terminal to destroy. */
  148. {
  149.     register PdevTerm *ptPtr = (PdevTerm *) ttyPdev;
  150.  
  151.     /*
  152.      * Close the terminal first, so that hangups can be sent to
  153.      * processes.
  154.      */
  155.  
  156.     Td_Delete(ptPtr->term);
  157.     Pdev_Close(ptPtr->pdev);
  158.     unlink(ptPtr->pdevName);
  159.     free(ptPtr->pdevName);
  160.     free((char *) ptPtr);
  161. }
  162.  
  163. /*
  164.  *----------------------------------------------------------------------
  165.  *
  166.  * PdevOpen --
  167.  *
  168.  *    This procedure is called back by the Pdev module whenever
  169.  *    a pseudo-terminal is being opened.
  170.  *
  171.  * Results:
  172.  *    None.
  173.  *
  174.  * Side effects:
  175.  *    None.
  176.  *
  177.  *----------------------------------------------------------------------
  178.  */
  179.  
  180.     /* ARGSUSED */
  181. static int
  182. PdevOpen(ptPtr, newStream, readBuffer, flags, procID, hostID,
  183.     uid, selectBitsPtr)
  184.     register PdevTerm *ptPtr;    /* Our information about the pdev. */
  185.     Pdev_Stream *newStream;    /* Service stream associated with the
  186.                  * new open. */
  187.     char *readBuffer;        /* Read buffer:  not used here. */
  188.     int flags;            /* Flags from open kernel call (not used). */
  189.     int procID;            /* Process doing open (not used). */
  190.     int hostID;            /* Host where process is running (not used). */
  191.     int uid;            /* Effective user id of pid (not used). */
  192.     int *selectBitsPtr;        /* Store select state of new stream here. */
  193. {
  194.     int result;
  195.     Boolean true = TRUE;
  196.     ReturnStatus status;
  197.  
  198.     newStream->clientData = (ClientData) ptPtr;
  199.     result = Td_Open(ptPtr->term, &ptPtr->selectState);
  200.     *selectBitsPtr = ptPtr->selectState;
  201.     status = Fs_IOControl(newStream->streamID, IOC_PDEV_WRITE_BEHIND,
  202.         sizeof(int), (Address) &true, 0, (Address) NULL);
  203.     if (status != SUCCESS) {
  204.     panic("PdevOpen couldn't enable write-behind:  %s",
  205.         Stat_GetMsg(status));
  206.     }
  207.     return result;
  208. }
  209.  
  210. /*
  211.  *----------------------------------------------------------------------
  212.  *
  213.  * PdevClose --
  214.  *
  215.  *    This procedure is called back by the Pdev module when all of
  216.  *    the streams corresponding to one "open" on a pseudo-terminal
  217.  *    have now been closed.
  218.  *
  219.  * Results:
  220.  *    Always returns zero.
  221.  *
  222.  * Side effects:
  223.  *    State in the terminal is updated.
  224.  *
  225.  *----------------------------------------------------------------------
  226.  */
  227.  
  228. static int
  229. PdevClose(streamPtr)
  230.     Pdev_Stream *streamPtr;    /* Service stream that is about to go away. */
  231. {
  232.     Td_Close(((PdevTerm *) streamPtr->clientData)->term);
  233.     return 0;
  234. }
  235.  
  236. /*
  237.  *----------------------------------------------------------------------
  238.  *
  239.  * PdevRead --
  240.  *
  241.  *    This procedure is called back by the Pdev module whenever
  242.  *    a client tries to read the pseudo-device associated with
  243.  *    a terminal.
  244.  *
  245.  * Results:
  246.  *    None.
  247.  *
  248.  * Side effects:
  249.  *    None.
  250.  *
  251.  *----------------------------------------------------------------------
  252.  */
  253.  
  254.     /* ARGSUSED */
  255. static int
  256. PdevRead(streamPtr, readPtr, freeItPtr, selectBitsPtr, sigPtr)
  257.     Pdev_Stream *streamPtr;    /* Service stream the client specified in its
  258.                  * kernel call. */
  259.     Pdev_RWParam *readPtr;    /* Read parameter block.  Indicates size,
  260.                  * buffer, plus various IDs */
  261.     Boolean *freeItPtr;        /* Not used here. */
  262.     int *selectBitsPtr;        /* Store new select state of terminal here. */
  263.     Pdev_Signal *sigPtr;
  264. {
  265.     int result;
  266.     register PdevTerm *ptPtr = (PdevTerm *) streamPtr->clientData;
  267.  
  268.     result = Td_GetCooked(ptPtr->term, readPtr->procID, readPtr->familyID,
  269.         &readPtr->length, readPtr->buffer, &sigPtr->signal,
  270.         &ptPtr->selectState);
  271.     *selectBitsPtr = ptPtr->selectState;
  272.     return result;
  273. }
  274.  
  275. /*
  276.  *----------------------------------------------------------------------
  277.  *
  278.  * PdevWrite --
  279.  *
  280.  *    This procedure is called back by the Pdev module whenever
  281.  *    a client tries to write the pseudo-device associated with
  282.  *    a terminal.  Note:  these writes are always asynchronous.
  283.  *
  284.  * Results:
  285.  *    None.
  286.  *
  287.  * Side effects:
  288.  *    None.
  289.  *
  290.  *----------------------------------------------------------------------
  291.  */
  292.  
  293.     /* ARGSUSED */
  294. static int
  295. PdevWrite(streamPtr, async, writePtr, selectBitsPtr, sigPtr)
  296.     Pdev_Stream *streamPtr;    /* Service stream the client specified in its
  297.                  * kernel call. */
  298.     int async;            /* Non-zero means this is an asynchronous
  299.                  * write request (should always be TRUE). */
  300.     Pdev_RWParam *writePtr;    /* Write parameter block.  Indicates size,
  301.                  * offset, and buffer, among other things */
  302.     int *selectBitsPtr;        /* Store new select state of terminal here. */
  303.     Pdev_Signal *sigPtr;    /* Signal to return, if any */
  304. {
  305.     register PdevTerm *ptPtr = (PdevTerm *) streamPtr->clientData;
  306.     int oldBits, result;
  307.  
  308.     oldBits = ptPtr->selectState;
  309.     result = Td_PutCooked(ptPtr->term, &writePtr->length, writePtr->buffer,
  310.         &sigPtr->signal, &ptPtr->selectState);
  311.     if (ptPtr->selectState != oldBits) {
  312.     Pdev_EnumStreams(ptPtr->pdev, ChangeReady,
  313.         (ClientData) ptPtr->selectState);
  314.     }
  315.     *selectBitsPtr = ptPtr->selectState;
  316.     return result;
  317. }
  318.  
  319. /*
  320.  *----------------------------------------------------------------------
  321.  *
  322.  * PdevIoctl --
  323.  *
  324.  *    This procedure is called back by the Pdev module whenever
  325.  *    a client tries to issue an ioctl on the pseudo-device associated
  326.  *    with a terminal.
  327.  *
  328.  * Results:
  329.  *    None.
  330.  *
  331.  * Side effects:
  332.  *    None.
  333.  *
  334.  *----------------------------------------------------------------------
  335.  */
  336.  
  337.     /* ARGSUSED */
  338. static int
  339. PdevIoctl(streamPtr, ioctlPtr, selectBitsPtr, sigPtr)
  340.     Pdev_Stream *streamPtr;    /* Service stream the client specified in its
  341.                  * kernel call. */
  342.     Pdev_IOCParam *ioctlPtr;    /* I/O control parameters */
  343.     int *selectBitsPtr;        /* Store new select state of terminal here. */
  344.     Pdev_Signal *sigPtr;    /* Returned signal, if any */
  345. {
  346.     register PdevTerm *ptPtr = (PdevTerm *) streamPtr->clientData;
  347.     int result;
  348.  
  349.     result = Td_ControlCooked(ptPtr->term, ioctlPtr->command,
  350.         ioctlPtr->format,
  351.         ioctlPtr->inBufSize, ioctlPtr->inBuffer,
  352.         &ioctlPtr->outBufSize, ioctlPtr->outBuffer,
  353.         &sigPtr->signal, &ptPtr->selectState);
  354.     *selectBitsPtr = ptPtr->selectState;
  355.     return result;
  356. }
  357.  
  358. /*
  359.  *----------------------------------------------------------------------
  360.  *
  361.  * CookedProc --
  362.  *
  363.  *    This procedure is called back by the Td module to inform
  364.  *    us of various things happening on the cooked side of the
  365.  *    terminal.
  366.  *
  367.  * Results:
  368.  *    The return value is the number of bytes of output data
  369.  *    stored at outBuffer (always 0 right now).
  370.  *
  371.  * Side effects:
  372.  *    Depends on the command;  read the code for details.
  373.  *
  374.  *----------------------------------------------------------------------
  375.  */
  376.  
  377.     /* ARGSUSED */
  378. static int
  379. CookedProc(ptPtr, command, inSize, inBuffer, outSize, outBuffer)
  380.     register PdevTerm *ptPtr;    /* Information about the pseudo-device
  381.                  * for the terminal. */
  382.     int command;        /* Identifies control operation being
  383.                  * invoked, e.g. TD_COOKED_SIGNAL. */
  384.     int inSize;            /* Number of bytes of input data available
  385.                  * to us. */
  386.     char *inBuffer;        /* Pointer to input data. */
  387.     int outSize;        /* Maximum number of bytes of output data
  388.                  * we can return to caller. */
  389.     char *outBuffer;        /* Area in which to store output data for
  390.                  * caller. */
  391. {
  392.     int result = 0;
  393.  
  394.     switch (command) {
  395.     case TD_COOKED_SIGNAL:
  396.         (void) Pdev_EnumStreams(ptPtr->pdev, SendSignal,
  397.             (ClientData) inBuffer);
  398.         break;
  399.     case TD_COOKED_READS_OK:
  400.         if (!(ptPtr->selectState & FS_READABLE)) {
  401.         ptPtr->selectState |= FS_READABLE;
  402.         (void) Pdev_EnumStreams(ptPtr->pdev, ChangeReady,
  403.             (ClientData) ptPtr->selectState);
  404.         }
  405.         break;
  406.     case TD_COOKED_WRITES_OK:
  407.         if (!(ptPtr->selectState & FS_WRITABLE)) {
  408.         ptPtr->selectState |= FS_WRITABLE;
  409.         (void) Pdev_EnumStreams(ptPtr->pdev, ChangeReady,
  410.             (ClientData) ptPtr->selectState);
  411.         }
  412.         break;
  413.     }
  414.     return result;
  415. }
  416.  
  417. /*
  418.  *----------------------------------------------------------------------
  419.  *
  420.  * ChangeReady --
  421.  *
  422.  *    This procedure is called back by Pdev_EnumStreams in order
  423.  *    to reset the readiness of all the streams associated with
  424.  *    a terminal.
  425.  *
  426.  * Results:
  427.  *    Always returns 0.
  428.  *
  429.  * Side effects:
  430.  *    The stream's select state is updated to match.
  431.  *
  432.  *----------------------------------------------------------------------
  433.  */
  434.  
  435. static int
  436. ChangeReady(streamPtr, selectState)
  437.     Pdev_Stream *streamPtr;        /* Information about the particular
  438.                      * stream. */
  439.     int selectState;            /* New select state for stream. */
  440. {
  441.     ReturnStatus status;
  442.  
  443.     status = Fs_IOControl(streamPtr->streamID, IOC_PDEV_READY,
  444.         sizeof(int), (Address) &selectState, 0, (Address) 0);
  445.     if (status != SUCCESS) {
  446.     panic("ChangeReady couldn't reset select state for pdev: %s",
  447.         Stat_GetMsg(status));
  448.     }
  449.     return 0;
  450. }
  451.  
  452. /*
  453.  *----------------------------------------------------------------------
  454.  *
  455.  * SendSignal --
  456.  *
  457.  *    This procedure is called back by Pdev_EnumStreams in order
  458.  *    to send a signal to the controlling process for a terminal.
  459.  *
  460.  * Results:
  461.  *    Always returns 1 to abort the enumeration after 1 stream
  462.  *    has been processed (there's no need to generate the signal
  463.  *    more than once).
  464.  *
  465.  * Side effects:
  466.  *    A signal is sent to the pseudo-device's controlling process (group).
  467.  *
  468.  *----------------------------------------------------------------------
  469.  */
  470.  
  471. static int
  472. SendSignal(streamPtr, sigInfoPtr)
  473.     Pdev_Stream *streamPtr;        /* Information about the particular
  474.                      * stream. */
  475.     Td_Signal *sigInfoPtr;        /* Information about signal to send. */
  476. {
  477.     ReturnStatus status;
  478.     Pdev_Signal sigInfo;
  479.  
  480.     status = Compat_UnixSignalToSprite(sigInfoPtr->sigNum, &sigInfo.signal);
  481.     if (status != SUCCESS) {
  482.     panic("SendSignal couldn't translate signal %d", sigInfoPtr->sigNum);
  483.     }
  484.     sigInfo.code = 0;
  485.  
  486.     /*
  487.      * Ignore errors in sending the signal:  they could happen because
  488.      * the user set a non-existent process group.
  489.      */
  490.  
  491.     (void) Fs_IOControl(streamPtr->streamID, IOC_PDEV_SIGNAL_OWNER,
  492.         sizeof(sigInfo), (Address) &sigInfo, 0, (Address) 0);
  493.     return 1;
  494. }
  495.